require 'digest/sha1'
class User < ActiveRecord::Base
  acts_as_ferret :fields => [:email, :bio, :fandoms]
  
  attr_protected :is_volcom, :is_otw, :is_translator, :activated_at, :activation_code, :crypted_password  
  
  has_many :userlinks
  has_many :userskills
  has_many :skills, :through => :userskills
  has_many :assignments
  has_many :jobs, :through => :assignments
  
  before_create :make_activation_code
  before_save :encrypt_password
  
  attr_accessor :remember_me
  attr_accessor :password
  attr_accessor :current_password
  attr_accessor :created_by_volcom
   
  # Max & min lengths for all fields 
  USERNAME_MIN_LENGTH = 3 
  USERNAME_MAX_LENGTH = 20 
  PASSWORD_MIN_LENGTH = 6 
  PASSWORD_MAX_LENGTH = 20 
  EMAIL_MAX_LENGTH = 50 
  ACTIVATION_CODE_SIZE = 20
  ACTIVATION_CODE_MAX_LENGTH = 20
  
  USERNAME_RANGE = USERNAME_MIN_LENGTH..USERNAME_MAX_LENGTH 
  PASSWORD_RANGE = PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH 
  
  # Text box sizes for display in views (.rhtml)
  USERNAME_SIZE = 20
  PASSWORD_SIZE = 20
  EMAIL_SIZE = 30
      
  # Number of users per page for pagination
  USERS_PER_PAGE = 20   
  USERS_SORT_BY = "username"      
      
  validates_presence_of   :username, :email
  validates_uniqueness_of :username, :email, :case_sensitive => false
  
  validates_length_of :username, :within => USERNAME_RANGE
  validates_length_of :email, :maximum => EMAIL_MAX_LENGTH

  validates_format_of :username,
                      :with => /^[A-Z0-9_]*$/i,
                      :message => "must contain only letters, numbers, and underscores".t
                      
  validates_format_of :email,
                      :with => /^[A-Z0-9_%\-\+\.]+@([A-Z0-9_\-]+\.)+[A-Z]{2,4}$/i,
                      :message => "must be a valid email address".t


  validates_presence_of     :password,                            :if => :password_required?
  validates_confirmation_of :password,                            :if => :password_required?
  validates_length_of       :password, :within => PASSWORD_RANGE, :if => :password_required?
  

    def validate
      errors.add(:password, "is not strong enough".t) unless password_strong_enough?
    end

    def password_strong_enough?
      return false if password == username
      return false if password =~ /^[a-z]+$/i
      return false if password =~ /^[0-9]+$/
      return true
    end
  
  public
  
  ### METHODS

  # Activates the user in the database.
  def activate!
    @activated = true
    self.activated_at = Time.now.utc
    self.activation_code = nil
    self.save!
  end

  # Returns true if the user has just been activated.
  def recently_activated?
    @activated
  end

  # Authenticate, making sure user is activated
  def self.authenticate(username, password)
    # Ignore records that have not been activated
    user = find( :first, :conditions => [ "username = ? AND activated_at IS NOT NULL", username ] )
    if (user && user.password_matches?(password))
      user
    else
      nil
    end
  end

 # Encrypts the password with the user salt
  def encrypt(password)
    self.class.encrypt(password, salt)
  end

  def password_matches?(password)
    crypted_password == encrypt(password)
  end


  # Log a user in
  def login!(session)
    session[:user_id] = self.id
    self.last_login = Time.now
  end
  
  # Log a user out
  def self.logout!(session, cookies)
    session[:user_id] = nil
    cookies.delete(:authorization_token)
  end
  
  # Clear the password view
  def clear_password_view!
    self.password = nil
    self.current_password = nil
  end
  
  # Should we remember this user?
  def remember_me?
    remember_me == "1"
  end
  
  # Remember a user's login
  def remember!(cookies)
    cookie_expiration = 10.years.from_now
    cookies[:remember_me] = { :value => "1", :expires => cookie_expiration }
    self.authorization_token = unique_identifier
    self.save!
    cookies[:authorization_token] = { :value => self.authorization_token,
                                      :expires => cookie_expiration }
  end
  
  # Forget a user's login
  def forget!(cookies)
    cookies.delete(:remember_me)
    cookies.delete(:authorization_token)
  end
  
  # Display a list of users
  def self.display_list(params)
    page = params[:page]

    if params[:per_page]
      per_page = per_page.match(/([0-9]+)/)[0] || USERS_PER_PAGE
    else
      per_page = USERS_PER_PAGE
    end
    
    if params[:sort] 
      sort = case params[:sort] 
             when "username" then "username"
             else USERS_SORT_BY   
             end    
    else
      sort = USERS_SORT_BY
    end
    
    paginate :page => page, :per_page => per_page, :order => sort
  end

  def self.find_all_by_skill(skill)
    Userskill.find_all_by_skill_id(skill.id).map {|userskill| User.find_by_id(userskill.user_id)}    
  end

  def self.find_all_by_job(job)
    Assignment.find_all_by_job_id(job.id).map {|assignment| User.find_by_id(assignment.user_id)}
  end

  ### SKILLS

  def add_skill(skill_id)
    userskill = Userskill.create(:user_id => id, :skill_id => skill_id)
    if userskill.valid? && userskill.save!
      return true
    end
    return false
  end

  def remove_skill(skill_id)
    if has_skill(skill_id)
      if Userskill.destroy Userskill.find_by_user_id_and_skill_id(id, skill_id)
        return true
      end
    end
    return false
  end

  def has_skill(skill_id)
    userskill = Userskill.find_by_user_id_and_skill_id(id, skill_id)
    if userskill.nil?
      return false
    else
      return true
    end
  end

  def get_all_skills
    userskills = Userskill.find_all_by_user_id(id) || []
    skills = userskills.map { |userskill| Skill.find_by_id(userskill.skill_id) } || []
  end
  
  def remove_all_skills
    skills = get_all_skills
    skills.each do |skill|
      remove_skill(skill.id)
    end    
  end

  ### ASSIGNMENTS/JOBS

  def add_job(job_id)
    assignment = Assignment.create(:user_id => id, :job_id => job_id)
    if assignment.valid? && assignment.save!
      return true
    end
    return false
  end

  def has_job(job_id)
    assignment = Assignment.find_by_user_id_and_job_id(id, job_id)
    if assignment.nil?
      return false
    end
    return true
  end

  def remove_job(job_id)
    if has_job(job_id)
      if Assignment.destroy Assignment.find_by_user_id_and_job_id(id, job_id)
        return true
      end
    end
    return false
  end
  
  def get_all_jobs
    assignments = Assignment.find_all_by_user_id(id)
    jobs = assignments.map {|assignment| Job.find_by_id(assignment.job_id) }
  end

  def remove_all_jobs
    jobs = get_all_jobs
    jobs.each do |job|
      remove_job(job.id)
    end
  end

  ### LINKS 

  def add_link(url)
    link = Userlink.create(:user_id => id, :url => url)
    if link.valid? && link.save!
      return true
    end
    return false
  end

  def has_link(url)
    link = Userlink.find_by_user_id_and_url(id, url)
    if link.nil?
      return false
    end
    return true
  end
  
  def remove_link(url)
    if has_link(url)
      if Userlink.destroy Userlink.find_by_user_id_and_url(id, url)
        return true
      end
    end
    return false
  end

  def get_all_links
    links = userlinks(true).map {|userlink| userlink.url}
  end

  def remove_all_links
    urls = get_all_links
    urls.each do |url|
      remove_link(url)
    end
  end

  # Delete all assignments and skills and links belonging to this user before destroying them
  def before_destroy
    remove_all_jobs
    remove_all_skills
    remove_all_links
  end

  # Encrypts some data with the salt.
  def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")  
  end

   protected
    # before filter 
    def encrypt_password
      return if password.blank?
      self.salt = unique_salt if new_record?
      self.crypted_password = encrypt(password)
    end

    def make_activation_code
      self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join ).first(ACTIVATION_CODE_MAX_LENGTH)
    end
          
    def password_required?
      crypted_password.blank? || !password.blank?
    end
  
    # Create a unique identifier for a user
    def unique_identifier
      Digest::SHA1.hexdigest("#{self.username}:#{self.activated_at}:#{self.email}")
    end

    # Create a unique salt for a user
    def unique_salt
      Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{self.username}--")  
    end
    
end
